/*-
  use bamp::ApiParser;
  use bamp::CppApiGen;
  use bamp::CApiGen;

  my $api = bamp::ApiParser->new("bamp.api", CLASSNAME);
  my $cpp = bamp::CppApiGen->new($api);
  my $c   = bamp::CApiGen->new($api);
  die "API parsing failed" if ${$api}{errors};

  my $class      = CLASSNAME; $class =~ s/::/_/g;
  my $incl_guard = "__".uc($class)."_WRAP_HPP__";
  my $struct     = lc($class);

  my $shortname  = $struct;
  $shortname =~ s/\w+?_//;

  my $interface = $shortname;
  $interface =~ s/^(.)/uc($1)/e;
  $interface =~ s/_(.)/uc($1)/e;
  my $cppclass = $interface."Ptr";

  my @includes = ();

  for my $p ($api->parents()) {
    $p =~ s/\w+:://;
    $p = lc($p);
    push(@includes, "bamp/".$p."_ptr.hpp");
  }

  push(@includes,
    "bamp/".$shortname."_api.h",
    "bamp/".$shortname."_api.hpp"
  );
*/
/*
 * /*= $cpp->header() */
 */

#ifndef /*$incl_guard*/
#define /*$incl_guard*/

#include <bamp/rc.hpp>
#include <bamp/exception.hpp>

/*- for ($api->includes("C++", @includes)) { */
#include </*$_*/>
/*- } */


/**
 * Utility macro to help declare and define a global constructor that
 * creates a new object of a given class. This is especially useful
 * when creating object-oriented dynamic libraries, such as plugins.
 */
#define /*+uc($class)*/_DECLARE(CLASSNAME, ...) \
   extern "C" { \
       void* create_/*$struct*/() \
       { \
           bamp::/*$cppclass*/ wrapper(new CLASSNAME()); \
           /*$struct*/_ref(wrapper.cptr()); \
           return wrapper.cptr(); \
       } \
       const char* /*$struct*/_types[] = {__VA_ARGS__, NULL}; \
   }

/**
 * Utility macro that create a symbol with the list of classes declared
 * in a dynamic library. There should be one entry for each use of
 * the /*+uc($class)*/ macro.
 */
#ifndef BAMP_CLASS_LIST
#define BAMP_CLASS_LIST(...) \
    extern "C" { \
        const char* class_list[] = {__VA_ARGS__}; \
    }
#endif

extern "C" {


/**
 * C adaptor function that deletes the /*$interface*/ object pointed to by
 * a generic context pointer.
 */
bamp_rc __/*$struct*/___dctor(void* ctx);

/**
 * C adaptor function that copies the whole /*$interface*/ object pointed to
 * by a generic context pointer.
 */
bamp_rc __/*$struct*/___clone(void** dst, void* src);

/*-
  for my $m ($api->all_methods()) {
    my $cppname = $cpp->meth_name($m);
*/
/**
 * C adaptor function for /*$interface*/::/*$cppname*/() method.
 */
/*+$c->declare($m, "__".$struct."_", "ctx", "void")*/;

/*- } */

} // extern "C"


namespace bamp
{
    class /*$cppclass*/ : public /*$interface*/
    {
    public:

        /**
         * Create an empty wrapper object. It must be assigned an
         * actual object before it is used.
         */
        /*$cppclass*/()
        : m_data(NULL)
        {
            // Nothing here
        }

        /**
         * Create a new wrapper around an actual /*$interface*/ object.
         * The wrapper can then be used to call functions of the object
         * or to pass it through a C interface using the pointer returned
         * by cptr(). This pointer can then be used to access the object
         * using a C interface or to initialize another /*$cppclass*/
         * wrapper around it.
         *
         * By initializing a /*$cppclass*/ with a pointer to an object,
         * you are giving it the ownership of that object. Objects held
         * by /*$cppclass*/ wrappers are reference-counted. They are deleted
         * when there are no more wrapper objects pointing to them.
         *
         * @param /*$shortname*/ pointer to an object to wrap
         */
        /*$cppclass*/(/*$interface*/* /*$shortname*/)
            : m_data(NULL), m_func(NULL)
        {
            BAMP_RC_THROW(/*$struct*/_new(&m_data));

            m_data->__ctx   = /*$shortname*/;
            m_data->__dctor = __/*$struct*/___dctor;
            m_data->__clone = __/*$struct*/___clone;

            /*-
              for my $m ($api->methods()) {
                my $name = $c->meth_name($m);
            */
            m_data->f./*$name*/ = __/*$struct*/_/*$name*/;
            /*- } */

            /*-
              for my $parent ($api->parents()) {
                for my $m ($api->class_methods($parent)) {
                  my $p = lc($parent);
                  $p =~ s/\w+:://;
                  my $name = $c->meth_name($m);
            */
            m_data->f./*$p*/./*$name*/ = __/*$struct*/_/*$name*/;
            /*- } } */

            m_func = &m_data->f;
        }

        /**
         * Wrap an existing object using its C pointer and specify which
         * function table to use. The function table is usually located at
         * some offset inside /*$struct*/.
         */
        /*$cppclass*/(/*$struct*/* /*$shortname*/, /*$struct*/_func_t* func)
            : m_data(/*$shortname*/), m_func(func)
        {
            BAMP_RC_THROW(/*$struct*/_ref(m_data));
        }

        /**
         * Wrap an existing object using its C pointer.
         * @param /*$shortname*/ C object pointer, as returned by cptr().
         */
        /*$cppclass*/(/*$struct*/* /*$shortname*/)
            : m_data(/*$shortname*/)
        {
            BAMP_RC_THROW(/*$struct*/_ref(m_data));
        }

        /**
         * Destroy the wrapper and decrement the reference counter of
         * the wrapped object. Destroy the object if there are no more
         * references to it.
         */
        virtual ~/*$cppclass*/()
        {
            if (m_data != NULL)
            {
                /*$struct*/_unref(m_data);
            }
        }

        /**
         * Copy a wrapper and increment its object's reference
         * counter.
         */
        /*$cppclass*/(const /*$cppclass*/& /*$shortname*/)
        {
            m_data = /*$shortname*/.m_data;
            m_func = /*$shortname*/.m_func;
            BAMP_RC_THROW(/*$struct*/_ref(m_data));
        }

        /*-
          for my $parent ($api->parents()) {
          my $cparent = lc($parent);
          $cparent =~ s/\w+:://;
          my $cppparent = $cparent."Ptr";
          $cppparent =~ s/^(.)/uc($1)/e;
          $cppparent =~ s/_(.)/uc($1)/e;
        */
        /**
         * Convert the object into an /*$cppparent*/ object. The new
         * /*$cppparent*/ points to the same object and shares the same
         * reference counter.
         */
        operator bamp::/*$cppparent*/()
        {
            return /*$cppparent*/((bamp_/*$cparent*/*)m_data, &m_data->f./*$cparent*/);
        }
        /*- } */


        /**
         * Copy a wrapper and increment its object's reference
         * counter. Also decrement the reference counter of the
         * previously held object and destroy it if there are no more
         * references to it.
         */
        /*$cppclass*/& operator=(const /*$cppclass*/& /*$shortname*/)
        {
            if (m_data != /*$shortname*/.m_data)
            {
                if (m_data != NULL)
                {
                    BAMP_RC_THROW(/*$struct*/_unref(m_data));
                }
                m_data = /*$shortname*/.m_data;
                m_func = /*$shortname*/.m_func;
                BAMP_RC_THROW(/*$struct*/_ref(m_data));
            }
            return *this;
        }

        /**
         * Perform a true copy of the object held by the wrapper. The
         * object returned will be a wrapper around a completely new
         * copy of the object, not a new reference to it.
         */
        virtual /*$interface*/* clone() const
        {
            void* copy;
            BAMP_RC_THROW(/*$struct*/_clone(&copy, m_data));
            return (/*$interface*/*)copy;
        }

        /*-
          for my $parent ($api->parents(), CLASSNAME) {
          for my $m ($api->class_methods($parent)) {
          my $p       = lc($parent); $p =~ s/\w+:://;
          my $f       = ($parent eq CLASSNAME) ? "" : "$p.";
          my $ret     = $api->first_out($m);
          my $cn      = $c->meth_name($m);
          my $n       = $cpp->meth_name($m);
          my @outlist = ($api->all_out($m));
          my @arglist = ("m_data->__ctx", $cpp->call_c_args($m, "ret_"));
        */
        /**
         * C++ method that calls a /*$interface*/::/*$n*/() C adaptor.
         */
        virtual /*+$cpp->declare($m)*/
        {
            /*-
              for my $out (@outlist) {
              my $varname = "ret_".${$out}[0];
              my $vartype = $c->arg_type($out);
              $vartype .= "*" if ${${$out}[4]}{C}{ref};
             */
            /*- if (${$out}[1] eq "io") { */
            /*$vartype*/ /*$varname*/ = /*+$cpp->arg_to_c($out, ${$out}[0])*/;
            /*- } else { */
            /*$vartype*/ /*$varname*/;
            /*- } } */
            BAMP_RC_THROW(m_func->/*$f*//*$cn*/(/*+ join(", ", @arglist)*/));
            /*-
              for my $out (@outlist) {
              next if defined $ret && $out == $ret;
            */
            /*${$out}[0]*/ = /*+$cpp->arg_from_c($out, "ret_".${$out}[0])*/;
            /*- } */
            /*- if (defined $ret) { */
            return /*+$cpp->arg_from_c($ret, "ret_".${$ret}[0])*/;
            /*- } */
        }

        /*- } } */

        /**
         * @return true if the wrapped object has not been set
         */
        bool isNull()
        {
            return m_data == 0;
        }

        operator bool()
        {
            return isNull();
        }

    public:

        /**
         * C structure name of the wrapped class.
         */
        static const char* cname;

        /**
         * C structure type of the wrapped class.
         */
        typedef /*$struct*/ ctype;

        virtual       ctype* cptr()       { return m_data; }
        virtual const ctype* cptr() const { return m_data; }


    private:
        
        /*$struct*/*        m_data;
        /*$struct*/_func_t* m_func;
    };
} // namespace bamp

#endif // /*$incl_guard*/
